<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="Emr50" id="Emr50"><span data-lake-id="u7ee1b4c9" id="u7ee1b4c9">典型回答</span></h1>
  <p data-lake-id="u42ee2031" id="u42ee2031"><br></p>
  <p data-lake-id="ud8e7b75b" id="ud8e7b75b"><span data-lake-id="uae392014" id="uae392014">在单体应用中，我们可以通过数据库的主键ID来生成唯一的ID，但是如果数据量变大，就需要进行分库分表，在分库分表之后，如何生成一个全局唯一的ID，就是一个关键的问题。</span></p>
  <p data-lake-id="u57924e41" id="u57924e41"><span data-lake-id="uff0f13cf" id="uff0f13cf">​</span><br></p>
  <p data-lake-id="u3a9cb54f" id="u3a9cb54f"><span data-lake-id="uc5932360" id="uc5932360">通常情况下，对于分布式ID来说，我们一般希望他具有以下几个特点：</span></p>
  <p data-lake-id="u9a818b78" id="u9a818b78"><span data-lake-id="uc62e8691" id="uc62e8691">​</span><br></p>
  <ul list="uf235ab94">
   <li fid="u58f72d23" data-lake-id="ucfb8069a" id="ucfb8069a"><span data-lake-id="u1eb1355a" id="u1eb1355a">全局唯一：必须保证全局唯一性，这个是最基本的要求。</span></li>
   <li fid="u58f72d23" data-lake-id="u958ae41a" id="u958ae41a"><span data-lake-id="ud7af5090" id="ud7af5090">高性能&amp;高可用：需要保证ID的生成是稳定且高效的。</span></li>
   <li fid="u58f72d23" data-lake-id="u4b0a78d6" id="u4b0a78d6"><span data-lake-id="u98920907" id="u98920907">递增：根据不同的业务情况，有的会要求生成的ID呈递增趋势，也有的要求必须单调递增（后一个ID必须比前一个大），也有的没有严格要求。</span></li>
  </ul>
  <p data-lake-id="u2f130c82" id="u2f130c82"><span data-lake-id="u8e11916e" id="u8e11916e">​</span><br></p>
  <p data-lake-id="ufa7a3e06" id="ufa7a3e06"><span data-lake-id="u7fccc3c9" id="u7fccc3c9">通常，在分布式ID的生成方案主要有以下6种：</span></p>
  <p data-lake-id="ub454ea45" id="ub454ea45"><br></p>
  <ul list="u48333bdf">
   <li fid="u37d3d000" data-lake-id="u70343806" id="u70343806"><span data-lake-id="ue96b88a0" id="ue96b88a0">UUID</span></li>
   <li fid="u37d3d000" data-lake-id="ua195fe0d" id="ua195fe0d"><span data-lake-id="u75daeaf3" id="u75daeaf3">数据库自增ID</span></li>
   <li fid="u37d3d000" data-lake-id="ue7c463d0" id="ue7c463d0"><span data-lake-id="uaa05d41f" id="uaa05d41f">号段模式</span></li>
   <li fid="u37d3d000" data-lake-id="u900ea142" id="u900ea142"><span data-lake-id="uc9d28c8d" id="uc9d28c8d">基于Redis 实现</span></li>
   <li fid="u37d3d000" data-lake-id="u5161d187" id="u5161d187"><span data-lake-id="ud52974e0" id="ud52974e0">雪花算法</span></li>
   <li fid="u37d3d000" data-lake-id="u83d8bf6d" id="u83d8bf6d"><span data-lake-id="ue6c1f449" id="ue6c1f449">第三方ID生成工具</span></li>
  </ul>
  <p data-lake-id="u809dea59" id="u809dea59"><br></p>
  <h1 data-lake-id="JWDzR" id="JWDzR"><span data-lake-id="u0a59ee3b" id="u0a59ee3b">扩展知识</span></h1>
  <h2 data-lake-id="q6Cix" id="q6Cix"><span data-lake-id="ud9df86b7" id="ud9df86b7">UUID</span></h2>
  <p data-lake-id="u616fad46" id="u616fad46"><br></p>
  <p data-lake-id="u78b68d60" id="u78b68d60"><span data-lake-id="u89b20d16" id="u89b20d16">UUID(Universally Unique Identifier)全局唯一标识符，是指在一台机器上生成的数字，它保证对在同一时空中的所有机器都是唯一的。</span></p>
  <p data-lake-id="uad9f9ecf" id="uad9f9ecf"><span data-lake-id="u23eb3d12" id="u23eb3d12">​</span><br></p>
  <p data-lake-id="ucfc93857" id="ucfc93857"><span data-lake-id="u8ed0fc1f" id="u8ed0fc1f">标准的UUID格式为：xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)，共32个字符，通常由以下几部分的组合而成：当前日期和时间，时钟序列，全局唯一的IEEE机器识别号</span></p>
  <p data-lake-id="uc9c3db41" id="uc9c3db41"><span data-lake-id="u38a0364c" id="u38a0364c">​</span><br></p>
  <p data-lake-id="ued3875ae" id="ued3875ae"><strong><span data-lake-id="ud8e55898" id="ud8e55898">UUID的优点就是他的性能比较高，不依赖网络，本地就可以生成，使用起来也比较简单。</span></strong></p>
  <p data-lake-id="u7b1d49e4" id="u7b1d49e4"><strong><span data-lake-id="u29cfe6f5" id="u29cfe6f5">​</span></strong><br></p>
  <p data-lake-id="u71e561ff" id="u71e561ff"><strong><span data-lake-id="u60d4474b" id="u60d4474b">但是他也有两个比较明显的缺点，那就是长度过长和没有任何含义</span></strong><span data-lake-id="u3321f15c" id="u3321f15c">。长度自然不必说，他有32位16进制数字。对于"550e8400-e29b-41d4-a716-446655440000"这个字符串来说，我想任何一个程序员都看不出其表达的含义。一旦使用它作为全局唯一标识，就意味着在日后的问题排查和开发调试过程中会遇到很大的困难。</span></p>
  <p data-lake-id="u2b4f8aca" id="u2b4f8aca" style="text-align: left"><br></p>
  <p data-lake-id="u0bb43a00" id="u0bb43a00"><br></p>
  <p data-lake-id="u10de98ea" id="u10de98ea"><span data-lake-id="ud00e99a3" id="ud00e99a3">用UUID当做分布式ID，存在着不适合范围查询、不方便展示以及查询效率低等问题。</span></p>
  <p data-lake-id="u7460ff8a" id="u7460ff8a"><span data-lake-id="u55a91824" id="u55a91824">​</span><br></p>
  <h2 data-lake-id="ZRXiF" id="ZRXiF"><span data-lake-id="uc78e9ac5" id="uc78e9ac5">数据库自增</span></h2>
  <p data-lake-id="ud07ba300" id="ud07ba300"><br></p>
  <p data-lake-id="u228f4abf" id="u228f4abf"><span data-lake-id="u146b25c4" id="u146b25c4">分布式ID也可以使用数据库的自增ID，但是这种实现中就要求一定是一个单库单表才能保证ID自增且不重复，这就带来了一个</span><strong><span data-lake-id="ud334d594" id="ud334d594">单点故障的问题</span></strong><span data-lake-id="u60c0a53c" id="u60c0a53c">。</span></p>
  <p data-lake-id="u811af4db" id="u811af4db"><span data-lake-id="u223f79db" id="u223f79db">​</span><br></p>
  <p data-lake-id="uff11a64e" id="uff11a64e"><span data-lake-id="u759d87b1" id="u759d87b1">一旦这个数据库挂了，那整个分布式ID的生成服务就挂了。而且还存在一个性能问题，如果高并发访问数据库的话，就会带来阻塞问题。</span></p>
  <p data-lake-id="u5b9e1b53" id="u5b9e1b53"><br></p>
  <h2 data-lake-id="U97R8" id="U97R8"><span data-lake-id="u568efc75" id="u568efc75">号段模式</span></h2>
  <p data-lake-id="u233cfcc4" id="u233cfcc4"><br></p>
  <p data-lake-id="ud9c77da1" id="ud9c77da1"><span data-lake-id="ua8139d16" id="ua8139d16">号段模式是在数据库的基础上，为了解决性能问题而产生的一种方案。他的意思就是每次去数据库中取ID的时候取出来一批，并放在缓存中，然后下一次生成新ID的时候就从缓存中取。这一批用完了再去数据库中拿新的。</span></p>
  <p data-lake-id="ub407b943" id="ub407b943"><span data-lake-id="u65f29fb8" id="u65f29fb8">​</span><br></p>
  <p data-lake-id="u0c59f8a3" id="u0c59f8a3"><span data-lake-id="uc84a0be6" id="uc84a0be6">而为了防止多个实例之间发生冲突，需要采用号段的方式，即给每个客户端发放的时候按号段分开，如客户端A取的号段是1-1000，客户端B取的是1001-2000，客户端C取的是2001-3000。当客户端A用完之后，再来取的时候取到的是3001-4000。</span></p>
  <p data-lake-id="u1d48c58f" id="u1d48c58f"><span data-lake-id="ua9cf91c4" id="ua9cf91c4">​</span><br></p>
  <p data-lake-id="uada7571a" id="uada7571a"><span data-lake-id="u4b10fefc" id="u4b10fefc">号段模式的好处是在同一个客户端中，生成的ID是顺序递增的。并且不需要频繁的访问数据库，也能提升获取ID的性能。缺点是没办法保证全局顺序递增，也存在数据库的单点故障问题。</span></p>
  <p data-lake-id="u9be3023d" id="u9be3023d"><span data-lake-id="u7fb194b0" id="u7fb194b0">​</span><br></p>
  <p data-lake-id="u4a408c8f" id="u4a408c8f"><span data-lake-id="ue4390494" id="ue4390494">其实很多分库分表的中间件的主键ID的生成，主要采用的也是号段模式，如TDDL Sequence</span></p>
  <p data-lake-id="u08d936ac" id="u08d936ac"><br></p>
  <h2 data-lake-id="ldxLE" id="ldxLE"><span data-lake-id="u1f818e43" id="u1f818e43">Redis 实现</span></h2>
  <p data-lake-id="u5a2b7d7a" id="u5a2b7d7a"><span data-lake-id="ua061ccc8" id="ua061ccc8">​</span><br></p>
  <p data-lake-id="u21c8b41e" id="u21c8b41e"><span data-lake-id="ub467cf77" id="ub467cf77">基于数据库可以实现，那么基于Redis也是可以的，我们可以依赖Redis的incr命令实现ID的原子性自增。</span></p>
  <p data-lake-id="u8a5d6750" id="u8a5d6750"><span data-lake-id="ub770e3ac" id="ub770e3ac">​</span><br></p>
  <p data-lake-id="u8c08108b" id="u8c08108b"><span data-lake-id="u4f684788" id="u4f684788">Redis的好处就是可以借助集群解决单点故障的问题，并且他基于内存性能也比较高。</span></p>
  <p data-lake-id="u2a4fd1ff" id="u2a4fd1ff"><span data-lake-id="u807dce5f" id="u807dce5f">​</span><br></p>
  <p data-lake-id="u23e2d318" id="u23e2d318"><span data-lake-id="ud60bea06" id="ud60bea06">但是Redis存在数据丢失的情况，无论是那种持久化机制，都无法完全避免。</span></p>
  <p data-lake-id="u24a92c40" id="u24a92c40"><span data-lake-id="uaea84691" id="uaea84691">​</span><br></p>
  <p data-lake-id="u83b0f3dc" id="u83b0f3dc"><span data-lake-id="u6603ee89" id="u6603ee89">​</span><br></p>
  <h2 data-lake-id="HXAwb" id="HXAwb"><span data-lake-id="u3ea14689" id="u3ea14689">雪花算法</span></h2>
  <p data-lake-id="ud0353b8c" id="ud0353b8c"><br></p>
  <p data-lake-id="uf7ec40bf" id="uf7ec40bf"><br></p>
  <h2 data-lake-id="zvzqq" id="zvzqq"><span data-lake-id="u88af0aa4" id="u88af0aa4">第三方工具</span></h2>
  <p data-lake-id="uc2a33a00" id="uc2a33a00"><br></p>
  <p data-lake-id="uda6b3fbd" id="uda6b3fbd"><span data-lake-id="u03eb8fef" id="u03eb8fef">除了以上方案以外，还有一些第三方的工具可以用来实现分布式ID，如百度的UidGenerator、美团的Leaf以及滴滴的Tinyid等等。</span></p>
  <p data-lake-id="u08ca0219" id="u08ca0219"><span data-lake-id="u266ca632" id="u266ca632">​</span><br></p>
  <p data-lake-id="ua51e911d" id="ua51e911d"><span data-lake-id="ud03e1217" id="ud03e1217">这些框架在功能上有的是整合了我们前面提到的多种实现方式，有的是针对不同的方式做了改进，如解决雪花算法的时钟拨回问题等。</span></p>
  <p data-lake-id="u692b237c" id="u692b237c" style="text-align: left"><span data-lake-id="uf43939e4" id="uf43939e4" class="lake-fontsize-12" style="color: rgb(34, 34, 34)"><br><br></span></p>
  <p data-lake-id="ufe05e427" id="ufe05e427" style="text-align: left"><br></p>
  <p data-lake-id="u2fe8e664" id="u2fe8e664"><span data-lake-id="u1378dda5" id="u1378dda5"><br><br></span></p>
 </body>
</html>